package com.atguigu.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.client.ProductFeignClient;
import com.atguigu.entity.*;
import com.atguigu.repository.ProductRepository;
import com.atguigu.result.RetVal;
import com.atguigu.search.*;
import com.atguigu.service.SearchService;
import lombok.SneakyThrows;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ProductRepository repository;
    @Resource
    private ProductFeignClient productFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    //商品上架  通过skuId 设置具体那个商品上架
    @Override
    public void onSale(Long skuId) {
        //将从数据查询的产品信息  封装到Product中 保存到es中
        Product product = new Product();
        //1.封装skuInfo的信息
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        if (skuInfo != null){
            product.setId(skuInfo.getId());
            product.setDefaultImage(skuInfo.getSkuDefaultImg());
            product.setProductName(skuInfo.getSkuName());
            //2.封装商品价格信息
            product.setPrice(skuInfo.getPrice().doubleValue());
            product.setCreateTime(new Date());

            //3.封装商品品牌的信息  根据skuId获取品牌id 在通过品牌id查询品牌信息
            BaseBrand brand = productFeignClient.getBrandById(skuInfo.getBrandId());
            if (brand != null){
                product.setBrandId(brand.getId());
                product.setBrandName(brand.getBrandName());
                product.setBrandLogoUrl(brand.getBrandLogoUrl());
            }

            //4.封装商品分类信息
            BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
            if (categoryView != null){
                product.setCategory1Id(categoryView.getCategory1Id());
                product.setCategory1Name(categoryView.getCategory1Name());
                product.setCategory2Id(categoryView.getCategory2Id());
                product.setCategory2Name(categoryView.getCategory2Name());
                product.setCategory3Id(categoryView.getCategory3Id());
                product.setCategory3Name(categoryView.getCategory3Name());
            }
        }

        //5.封装商品的平台属性信息
        List<PlatformPropertyKey> platformPropertyList = productFeignClient.getPlatformPropertyBySkuId(skuId);
        if (!CollectionUtils.isEmpty(platformPropertyList)){
            List<SearchPlatformProperty> searchPlatformPropertyList =
                    platformPropertyList.stream().map((platformProperty)->{

                SearchPlatformProperty searchPlatformProperty = new SearchPlatformProperty();

                searchPlatformProperty.setPropertyKeyId(platformProperty.getId());
                searchPlatformProperty.setPropertyKey(platformProperty.getPropertyKey());
                List<PlatformPropertyValue> propertyValueList = platformProperty.getPropertyValueList();
                //因为确定了平台属性id 和 平台属性名称 那么平台属性值就为一个了
                searchPlatformProperty.setPropertyValue(propertyValueList.get(0).getPropertyValue());
                return searchPlatformProperty;
            }).collect(Collectors.toList());

            product.setPlatformProperty(searchPlatformPropertyList);
        }
        repository.save(product);
    }
    //商品下架  通过skuId 设置具体那个商品下架
    @Override
    public void offSale(Long skuId) {
        repository.deleteById(skuId);
    }
    //根据商品的访问量 设置商品的热度
    @Override
    public void incrHotScore(Long skuId) {
        //定义一个缓存key
        String key = "sku:incrHotScore:"+skuId;
        //利用ZSet集合中的评分  来定义商品的热度  让访问一次 浏览量+1
        Double count = redisTemplate.opsForZSet().incrementScore(key, skuId, 1);
        //判断加了几次  每6次放入es中一次
        if (count%6 == 0){
            //获取es的信息 对热度进行修改
            Optional<Product> optional = repository.findById(skuId);
            Product product = optional.get();
            //将double的count 四舍五入为整数
            product.setHotScore(Math.round(count));
            //保存es
            repository.save(product);
        }
    }
    //商品搜索功能
    @SneakyThrows //相当于是try catch
    @Override
    public SearchResponseVo searchProduct(SearchParam searchParam) {
        //1.商品搜索的DSL语句
        SearchRequest searchRequest = this.searchProductDSL(searchParam);
        //2.通过restHighLevelClient实现查询
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //3.将查询到的数据封装到SearchResponseVo中
        SearchResponseVo searchResponseVo = this.parseSearchResponse(searchResponse);
        //4.封装其他信息参数
        searchResponseVo.setPageNo(searchParam.getPageNo());
        searchResponseVo.setPageSize(searchParam.getPageSize());
        //5.设置  总页数 = 总记录数/每页显示数量
        boolean total = searchResponseVo.getTotal()%searchParam.getPageSize()==0;
        if (total){
            searchResponseVo.setTotalPages(searchResponseVo.getTotal()/searchParam.getPageSize());
        }else {//除不尽 则多加一页
            searchResponseVo.setTotalPages(searchResponseVo.getTotal()/searchParam.getPageSize()+1);
        }
        return searchResponseVo;
    }
    //查询到的数据解析 并且封装到 SearchResponseVo中
    private SearchResponseVo parseSearchResponse(SearchResponse searchResponse) {
        //最终返回的结果
        SearchResponseVo searchResponseVo = new SearchResponseVo();
        //1.拿到商品的基本信息
        SearchHits hits = searchResponse.getHits();
        SearchHit[] secondHits  = hits.getHits();
        List<Product> products = new ArrayList<>();
        if (secondHits.length>0 && secondHits != null){
            for (SearchHit secondHit : secondHits) {
                //将得到的商品信息装为product对象
                Product product = JSONObject.parseObject(secondHit.getSourceAsString(), Product.class);
                //获取高亮信息
                HighlightField productNameHigh = secondHit.getHighlightFields().get("productName");
                if (productNameHigh != null){
                    Text[] fragments = productNameHigh.getFragments();
                    //再封装到product中
                    product.setProductName(fragments[0].string());
                }
                products.add(product);
            }
        }
        searchResponseVo.setProductList(products);
        //2.共计多少条数据
        searchResponseVo.setTotal(hits.totalHits);
        //3.获取品牌信息  从聚合信息中获取
        ParsedLongTerms brandIdAgg = searchResponse.getAggregations().get("brandIdAgg");
        List<? extends Terms.Bucket> brandIdBuckets = brandIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(brandIdBuckets)){
            List<SearchBrandVo> brandVoList = brandIdBuckets.stream().map((brandBucket) -> {
                //返回结果
                SearchBrandVo searchBrandVo = new SearchBrandVo();

                String brandId = brandBucket.getKeyAsString();
                searchBrandVo.setBrandId(Long.parseLong(brandId));

                //获取品牌的名称 只有一条记录
                ParsedStringTerms brandNameAgg = brandBucket.getAggregations().get("brandNameAgg");
                String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
                searchBrandVo.setBrandName(brandName);

                ParsedStringTerms brandLogoUrlAgg = brandBucket.getAggregations().get("brandLogoUrlAgg");
                //获取品牌的logoUrl
                String brandLogoUrl = brandLogoUrlAgg.getBuckets().get(0).getKeyAsString();
                searchBrandVo.setBrandLogoUrl(brandLogoUrl);

                return searchBrandVo;
            }).collect(Collectors.toList());
            searchResponseVo.setBrandVoList(brandVoList);
        }
        //4.获取平台属性信息
        ParsedNested platformPropertyAgg = searchResponse.getAggregations().get("platformPropertyAgg");
        ParsedLongTerms propertyKeyIdAgg = platformPropertyAgg.getAggregations().get("propertyKeyIdAgg");
        List<? extends Terms.Bucket> propertyKeyIdBuckets = propertyKeyIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(propertyKeyIdBuckets)){
            List<SearchPlatformPropertyVo> platformPropertyList = propertyKeyIdBuckets.stream().map((propertyKeyIdBucket) -> {
                //封装到SearchPlatformPropertyVo中
                SearchPlatformPropertyVo searchPlatformPropertyVo = new SearchPlatformPropertyVo();
                //获取平台属性key的id  并赋值
                Number propertyKeyId = propertyKeyIdBucket.getKeyAsNumber();
                searchPlatformPropertyVo.setPropertyKeyId(propertyKeyId.longValue());
                //再进一步获取平台属性key的值
                ParsedStringTerms propertyKeyAgg = propertyKeyIdBucket.getAggregations().get("propertyKeyAgg");
                String propertyKey = propertyKeyAgg.getBuckets().get(0).getKeyAsString();
                searchPlatformPropertyVo.setPropertyKey(propertyKey);
                //再获取平台属性value的值
                ParsedStringTerms propertyValueAgg = propertyKeyIdBucket.getAggregations().get("propertyValueAgg");
                List<? extends Terms.Bucket> propertyValueBuckets = propertyValueAgg.getBuckets();
                if (!CollectionUtils.isEmpty(propertyValueBuckets)) {
                    //通过Terms.Bucket::getKeyAsString 获取数据 并收集起来放入到一个集合中
                    List<String> propertyValueList = propertyValueBuckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
                    //将结果放入到searchPlatformPropertyVo中
                    searchPlatformPropertyVo.setPropertyValueList(propertyValueList);
                }
                return searchPlatformPropertyVo;
            }).collect(Collectors.toList());
            //再将最终的到的平台属性集合platformPropertyList放入到searchResponseVo中
            searchResponseVo.setPlatformPropertyList(platformPropertyList);
        }
        return searchResponseVo;
    }

    private SearchRequest searchProductDSL(SearchParam searchParam) {

        BoolQueryBuilder firstBoolQuery = QueryBuilders.boolQuery();
        Long category1Id = searchParam.getCategory1Id();
        Long category2Id = searchParam.getCategory2Id();
        Long category3Id = searchParam.getCategory3Id();

        //1.分类id的筛选
        if (category1Id != null) {
            TermQueryBuilder category1IdQuery = QueryBuilders.termQuery("category1Id", category1Id);
            firstBoolQuery.filter(category1IdQuery);
        }
        if (category2Id != null) {
            TermQueryBuilder category2IdQuery = QueryBuilders.termQuery("category2Id", category2Id);
            firstBoolQuery.filter(category2IdQuery);
        }
        if (category3Id != null) {
            TermQueryBuilder category3IdQuery = QueryBuilders.termQuery("category3Id", category3Id);
            firstBoolQuery.filter(category3IdQuery);
        }

        //2.根据品牌的id筛选
        String brandName = searchParam.getBrandName();
        if (!StringUtils.isEmpty(brandName)){
            String[] split = brandName.split(":");
            if (split.length == 2) {
                TermQueryBuilder brandIdQuery = QueryBuilders.termQuery("brandId", split[0]);
                firstBoolQuery.filter(brandIdQuery);
            }
        }

        //3.嵌套查询  查询平台属性 前端传的数据为 例:prop=1:cpu型号:三星Exynos
        String[] props = searchParam.getProps();
        if (null != props && props.length > 0){
            for (String prop : props) {
                String[] propSplit = prop.split(":");
                if (propSplit.length == 3){
                    BoolQueryBuilder secondBoolQuery = QueryBuilders.boolQuery();

                    BoolQueryBuilder thirdBoolQuery = QueryBuilders.boolQuery();
                    thirdBoolQuery.must(QueryBuilders.termQuery("platformProperty.propertyKeyId",propSplit[0]));
                    thirdBoolQuery.must(QueryBuilders.termQuery("platformProperty.propertyValue",propSplit[1]));

                    firstBoolQuery.filter(secondBoolQuery.must(QueryBuilders.nestedQuery("platformProperty",thirdBoolQuery, ScoreMode.None)));
                }
            }
        }

        //4.通过搜索栏中传入的商品的名称或品牌进行查询
        String keyword = searchParam.getKeyword();
        if (!StringUtils.isEmpty(keyword)){
            firstBoolQuery.must(QueryBuilders.matchQuery("productName",keyword).operator(Operator.AND));
        }

        //将数据放入到SearchSourceBuilder中
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(firstBoolQuery);
        //5.分页
        Integer pageNo = (searchParam.getPageNo()-1)*searchParam.getPageSize();//当前页=(当前页-1)*每页显示记录数
        Integer pageSize = searchParam.getPageSize();//每页显示数据个数
        sourceBuilder.from(pageNo);
        sourceBuilder.size(pageSize);

        //6.排序 前端传的数据为例:order=1:xxx  综合为:浏览量 降序desc  价格排序:升序asc
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)){
            //排序 1为综合排序  2为价格排序
            String[] orderSplit = order.split(":");
            if (orderSplit.length==2){
                String filedName = null;
                //利用Switch case进行判断
                switch (orderSplit[0]){
                    case "1":
                        filedName = "hotScore";
                        break;
                    case "2":
                        filedName = "price";
                        break;
                }
                //给定  综合(浏览量)为: 降序desc  价格排序:升序asc
                sourceBuilder.sort(filedName, "asc".equals(orderSplit[1])?SortOrder.ASC:SortOrder.DESC);
            }
        }else{
            //默认为热度降序排序
            sourceBuilder.sort("hotScore",SortOrder.DESC);
        }
        //7.对搜索词进行高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("productName");
        highlightBuilder.preTags("<span style=color:red>");
        highlightBuilder.postTags("</span>");
        sourceBuilder.highlighter(highlightBuilder);

        //8.商品品牌的聚合
        TermsAggregationBuilder brandIdAgg = AggregationBuilders.terms("brandIdAgg").field("brandId")
                .subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"))
                .subAggregation(AggregationBuilders.terms("brandLogoUrlAgg").field("brandLogoUrl"));
        sourceBuilder.aggregation(brandIdAgg);
        //9.平台属性的聚合
        sourceBuilder.aggregation(
                AggregationBuilders.nested("platformPropertyAgg","platformProperty")
                .subAggregation(AggregationBuilders.terms("propertyKeyIdAgg").field("platformProperty.propertyKeyId")
                        .subAggregation(AggregationBuilders.terms("propertyKeyAgg").field("platformProperty.propertyKey"))
                        .subAggregation(AggregationBuilders.terms("propertyValueAgg").field("platformProperty.propertyValue"))));
        //10.设置查询完后页面需要返回的字段
        sourceBuilder.fetchSource(new String[]{"id", "defaultImage", "productName", "price"},null);
        //11.设置es查询那个索引index 那个类型type
        SearchRequest searchRequest = new SearchRequest("product");
        searchRequest.types("info");
        searchRequest.source(sourceBuilder);

        return searchRequest;
    }
}
